<?php
declare (strict_types = 0);
namespace app\controller;

use think\facade\Db;
use think\facade\Request;
use think\facade\Session;
use app\controller\Token;
use app\controller\AesUtil;
use app\controller\Acc;
use app\Common;
use dh2y\qrcode\QRcode;

class Wxpay{
    /*
    *支付请求
    *客户端发起支付请求，上传支付金额挤微信个人"code"
    *调用微信个人信息接口，获得openid.
    */
    public function get_openid(){
        $all = $_REQUEST;
        if(empty($all['code'])){
            return json(['code'=>0,'msg'=>'Invalid parameter [code] or [money].']);
        }

        $appid = 'wx9cdc7cd9c8f52222';
        $secret = "bf4f31c22a2a6f948ee85985f5dd9ee1";
        $url = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
        $url = sprintf($url,$appid,$secret,$all['code']);
        $str = file_get_contents($url);
        $json = json_decode($str);
        $arr = get_object_vars($json);

        if(empty($arr['openid']) || empty($arr['session_key'])){
            return json(['code'=>0,'msg'=>'Error of openid get.']);
        }
        
        return json(['code'=>1,'openid'=>$arr['openid']]);

        //return json($this->wx_pay($arr['openid'],$all['money']));
    }


    //微信扫码支付wx_native_pay
    public function wx_native_pay(){

        $all= Request::param();
        
        // if(empty($all['customer_id']) || empty($all['money']) || !is_numeric($all['money'] )) {
        //     return json(['code'=>0,'msg'=>'参数错误,customer_id,money']);
        // }
        $money = ((int)$all['money']*100); //正式环境要x100,因为单位是分。
        $money=1;
        $voucher = !empty($all['voucher'])?$all['voucher']:'';
        $memo = !empty($all['memo'])?$all['memo']:'一般收款';

        //$cust=Db::table('customer')->find($all['customer_id']);

        // $cid = $customer['id'];
        // $name = $customer['name'];
        // $tmp_arr =[
        //     'customer_id'=>$cid,
        //     'money'=>$money,
        //     'openid'=>'',
        //     'memo'=>$memo,
        //     'create_time'=>date('Y-m-d H:i:s'),
        //     'voucher'=>$voucher
        // ];

        //写一个中转表，微信支付回调的时候从这里找出对应的客户，用来写帐单记录
        //Db::table('wxpay_openid')->insert($tmp_arr);


        // 商户相关配置
        $merchantId = '1659706467'; // 商户号 
        $merchantSerialNumber = '240BEF403885A24594D6D5CBBD8CA22AA8D6CD00'; // 商户API证书序列号,在API证书里面
        $filepath = "attach/cert/apiclient_key.pem"; //私钥在本地的位置
        $mch_private_key = self::getPrivateKey($filepath);
 
        $appid = 'wx9cdc7cd9c8f52222'; //小程序appid
        $out_trade_no = 'Native_'.date('YmdHis', time()) . rand(1000, 9999);

        //签名主体数据
        $data = [
            "appid" => $appid,
            "mchid" => $merchantId,
            "description" => $memo,
            'out_trade_no' => $out_trade_no,
            'notify_url' => 'https://xjpa666.cn:4433/wxpay/sharenotify',  //回调地址
            "amount" => [
                "total" => $money,
                "currency" => "CNY"
            ],
        ];

        $data = json_encode($data);

        $timestamp = time();
        $nonce = date('YmdHis', time()) . rand(1000, 9999);
        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/native';
        $url_parts = parse_url($url);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
 
        //签名串内容       
        $message = 'POST' . "\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $data . "\n";
        
        //生成签名值
        openssl_sign($message, $signature, $mch_private_key, "sha256WithRSAEncryption");
        $sign = base64_encode($signature);
        
        //认证类型
        $schema = 'WECHATPAY2-SHA256-RSA2048'; 

        //签名信息
        $sign_info = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $merchantId, $nonce, $timestamp, $merchantSerialNumber, $sign);
        
        //生成请求header信息,格式：Authorization:认证类型 签名信息
        $header = "Authorization: " . $schema . " " . $sign_info;
 
        //发起支付请求
        $res = $this->http_post($url, $header, $data);
        $arr = json_decode($res, true);

        if(isset($arr['code_url'])){
            echo "<img src='".$this->thinkQRcode($arr['code_url'])."' />"; return;
            //return $arr['code_url'];      //收款二维码
        }else{
            return json($arr);
        }

    }


    //微信统一下单接口
    public function wx_pay($openid,$money){
        $all= Request::param();
        
        if(empty($all['openid']) || empty($all['money']) || !is_numeric($all['money'])) {
            return json(['code'=>0,'msg'=>'Invalid parameter.']);
        }
        $money = ((int)$all['money']*100); //正式环境要x100,因为单位是分。
        //$money=1;  //测试金额

        $openid = $all['openid'];
        $voucher = !empty($all['voucher'])?$all['voucher']:'';
        $memo = !empty($all['memo'])?$all['memo']:'一般收款';

        if(!empty($all['token'])){
            $token =new token();
            $res = $token->check_token($all['token']);
            if($res['code']==0) return json($res);

            $cid = $res['customer']['id'];
            $name = $res['customer']['name'];
            $tmp_arr =[
                'customer_id'=>$cid,
                'money'=>$money,
                'openid'=>$openid,
                'memo'=>$memo,
                'create_time'=>date('Y-m-d H:i:s'),
                'status'=>1,
                'voucher'=>$voucher
            ];

            //写一个中转表，微信支付回调的时候从这里找出对应的客户，用来写帐单记录
            Db::table('wxpay_openid')->insert($tmp_arr);

        }else{
            $cid='';
            $name='';
        }


        // 商户相关配置
        $merchantId = '1659706467'; // 商户号 
        $merchantSerialNumber = '240BEF403885A24594D6D5CBBD8CA22AA8D6CD00'; // 商户API证书序列号 
        $filepath = "attach/cert/apiclient_key.pem"; //私钥在本地的位置
        $mch_private_key = self::getPrivateKey($filepath);
        $appid = 'wx9cdc7cd9c8f52222'; //小程序appid
        $out_trade_no = 'wyshop_'.date('YmdHis', time()) . rand(1000, 9999);

        //签名主体数据
        $data = [
            "appid" => $appid,
            "mchid" => $merchantId,
            "description" => $memo,
            'out_trade_no' => $out_trade_no,
            'notify_url' => 'https://xjpa666.cn:4433/wxpay/sharenotify',  //回调地址
            "amount" => [
                "total" => $money,
                "currency" => "CNY"
            ],
            "payer" => [
                "openid" => $openid //用户openid
            ]
        ];
        $data = json_encode($data);

        $timestamp = time();
        $nonce = date('YmdHis', time()) . rand(1000, 9999);
        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';
        $url_parts = parse_url($url);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
 
        //签名串内容       
        $message = 'POST' . "\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $data . "\n";
        
        //生成签名值
        openssl_sign($message, $signature, $mch_private_key, "sha256WithRSAEncryption");
        $sign = base64_encode($signature);
        
        //认证类型
        $schema = 'WECHATPAY2-SHA256-RSA2048'; 

        //签名信息
        $sign_info = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $merchantId, $nonce, $timestamp, $merchantSerialNumber, $sign);
        
        //生成请求header信息,格式：Authorization:认证类型 签名信息
        $header = "Authorization: " . $schema . " " . $sign_info;
 
        //发起支付请求
        $res = $this->http_post($url, $header, $data);
        $arr = json_decode($res, true);
        //return json($arr);

        $time = time();
        $str = time() . round('1000', '9999');
        $prepay = 'prepay_id=' . $arr['prepay_id'];
 
        $message1 = $appid . "\n" .
            $time . "\n" .
            $str . "\n" .
            $prepay . "\n";
 
        //生成验证签名，发给客户端
        openssl_sign($message1, $signature, $mch_private_key, "sha256WithRSAEncryption");
        $sign1 = base64_encode($signature);
        
        //定义的返回数据，支付发起用。
        $data = array(
            'timeStamp' => $time,
            'nonce' => $str,
            'prepay_id' => $prepay,
            'sign' => $sign1,
            'appid' => $appid,
            'token' =>$all['token'],// $this->randomString(32),
            'sign_type' =>"sha256WithRSAEncryption",
            'cid'=>$cid,
            'name'=>$name,

        );
        return json($data);
    }
 
    //post提交，用此方法发起支付,curl
    function http_post($url, $header, $data)
    {
        $headers[] = "Accept:application/json";
        $headers[] = "Content-Type:application/json";
        $headers[] = "User-Agent:application/json";
        $headers[] = $header;
 
        $curl = curl_init(); // 启动一个CURL会话
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);  // 从证书中检查SSL加密算法是否存在
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        $tmpInfo = curl_exec($curl);
        //关闭URL请求
        curl_close($curl);
        return $tmpInfo;
    }
 

    
    //产生acc记录
    public function acc_log($openid,$money){
        $cust = Db::query("select * from wxpay_openid where openid=? and status=1 order by create_time desc limit 1",[$openid]);
        if(!count($cust)) return 'error1';

        $arr = $cust[0];
        $cid = $arr['customer_id'];
        $memo = trim($arr['memo']);

        //订单处理,付款后更新订单状态
        $memo_arr=explode(":",$memo);
        if(count($memo_arr)==2 && $memo_arr[1]=='订单付款'){
            $order_no= $memo_arr[0];                        //订单号
            Db::execute('update `order` set pay_time=?,status=2 where order_no=?',[date('Y-m-d h:i:s'),$memo_arr[0]]);
            Db::execute('update wxpay_openid set status=2 where id=?',[$arr['id']]);
        }
        //如果使用了优惠券，做核销状态。
        if($arr['voucher']>0){
            Db::execute("update voucher_mine set status=3,use_time=? where sn=?",[date('Y-m-d H:i:s'),$all['voucher']]);
        }

        $customer = Db::table('customer')->find($cid);
        if(!$customer) return 'error2';

        $acc_log=array(
            'type'=>3,
            'customer_id'=>$cid,
            'dept_id'=>$customer['dept_id'],
            'money'=>$money,
            'memo'=>!empty($memo)?$memo:'',
            'payment'=>'wx',
            'creater'=>2,
            'create_time'=>date('Y-m-d H:i:s'),
            'check_by'=>2,
            'check_time'=>date('Y-m-d H:i:s'),
            'status'=>2
        );
        $res =Db::table('acc_log')->insert($acc_log);

        //交学费的时候，计算推广佣金
        // if(strpos($memo,'证学费')){
        //     Db::execute('update customer set fee_time=? where id=?',[date('Y-m-d H:i:s'),$customer['id']]);
        //     $acc= new Acc();
        //     $msg = $acc->comm_for_inviter($customer); 
        // }

        if($res) return 'ok';
        return 0;

    }

    public static function randomString($length = 10) { 
      $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
      $randStr = ''; 
      for ($i = 0; $i < $length; $i++) { 
        $randStr .= $characters[rand(0, strlen($characters) - 1)]; 
      } 
      return $randStr; 
    }    


    public static function getPrivateKey($filepath = ''){
        if (empty($filepath)) {
            //私钥位置
            $cert_dir = 'attach/cert/';
            $filepath = $cert_dir."apiclient_key.pem";
        }
        return openssl_get_privatekey(file_get_contents($filepath));
    }

    //think-qrcode
    public function thinkQRcode($data=''){
        // //设置缓存地址
        // config('qrcode.cache_dir','../runtime/cache/qrcode');
        // //设置背景图
        // config('qrcode.background','static/image/icon_cover.png');

        $code = new QRcode();
        $res = $code->png($data,false,6,'L')->logo('upload/logo.png')->entry();
        return $res;
    }


    /**
     * 生成二维码
     * @return string
     */
    public function qr_code($data=''){
        header("Content-type:image/png");
        require "../extend/phpqrcode/phpqrcode.php";
        $qRcode = new \QRcode();
        //$data = 'https://www.baidu.com/';//网址或者是文本内容
        // 纠错级别：L、M、Q、H
        $level = 'L';
        // 点的大小：1到10,用于手机端4就可以了
        $size = 4;
        // 生成的文件名
        $qRcode->png($data, false, $level, $size);
        $imagestring = base64_encode(ob_get_contents());
        ob_end_clean();
        return "<img src='data:image/png;base64,{$imagestring}'  />";
    }


    //支付回调函数，微信支付调用
    public function sharenotify(){
        $getCallBackData = file_get_contents('php://input'); //接收来自微信的回调数据
        $str = Common::randomString(10);                     //一个随机串
        if(!empty($getCallBackData)){
            $f_name='upload/wxpay_callBack/callback_'.$str.'.json';
            file_put_contents($f_name,$getCallBackData); //将接收到的数据存入callBack.json文件中
        }else{
            header('Status: 400');
            return json(['code'=>'fail','msg'=>'失败']);
        }

        $getReturnData = file_get_contents($f_name);  //将返回的json数据赋值给变量
        //unlink($f_name);
        $arr = json_decode($getReturnData, true);  //将变量由json类型数据转换为数组
        
        $associatedData = $arr['resource']['associated_data'];  //获取associated_data数据,附加数据
        $nonceStr = $arr['resource']['nonce'];                  //获取nonce数据,加密使用的随机串
        $ciphertext = $arr['resource']['ciphertext'];           //获取ciphertext数据,base64编码后的数据密文

        $aes = new AesUtil();
        $result = $aes->decryptToString($associatedData,$nonceStr,$ciphertext); //调用微信官方给出的方法将解密后的数据赋值给变量
        $array_data = json_decode($result,true);  //将解密后的数据转换为数组

        //把解密后的支付结果写入文件保存
        file_put_contents('upload/wxpay_callBack/dec_'.$str.'.json','decrypt:'.$result); //将数据以JSON格式储存到decryptReturnData.json文件中

        if($array_data['trade_state']=='SUCCESS'){
            $openid=$array_data['payer']['openid'];
            
            $money = $array_data['amount']['total'];
            $money=($money/100);

            $acc = $this->acc_log($openid,$money);

            header('Status: 200');
            return json(['msg'=>'ok']);

        }else{
            header('Status: 400');
            return json(['code'=>'fail','msg'=>'失败']);
        }



    }    

}